/*
  * Copyright (c) 2022 Huawei Device Co., Ltd.
  *
 * Based on DaoMaster.java written by
 * Copyright (C) 2011-2016 Markus Junginger, greenrobot (http://greenrobot.org)
 *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
    *
  * http://www.apache.org/licenses/LICENSE-2.0
    *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */

import { AbstractDao } from './AbstractDao';
import { AbstractDaoMaster } from './AbstractDaoMaster';
import { StandardDatabase } from './database/StandardDatabase';
import { Database } from './database/Database';
import { DatabaseOpenHelper } from './database/DatabaseOpenHelper';
import { IdentityScopeType } from './identityscope/IdentityScopeType';
import { BaseDao } from './BaseDao';
import { DaoSession } from './DaoSession';
import { GlobalContext } from './GlobalContext';
import { StorageUtils } from './StorageUtils';
import { DaoTraceSpace, DaoTraceUtil } from './trace/DaoTraceUtil';

var schema_version: number = 1;

// THIS CODE IS GENERATED BY dataORM, DO NOT EDIT.
/**
 * Master of DAO (schema version 1000): knows all DAOs.
 */
export class DaoMaster extends AbstractDaoMaster {
  /** Creates underlying database table using DAOs. */
  public static async createAllTables(db: Database, ifNotExists: boolean): Promise<void> {
    const uniqueKeyForEntities = `${GlobalContext.KEY_CLS_ARRAY}_${db.name}${db.customDir ? '_'+db.customDir : ''}`;
    let entities = GlobalContext.getContext().getValue(uniqueKeyForEntities) as Object[];
    if (entities) {
      DaoTraceUtil.startInfo("DaoMaster.createAllTables", DaoTraceSpace.TraceType.CRUD);
      for (let entity of entities) {
        await BaseDao.createTable(db, ifNotExists, entity);
      }
      DaoTraceUtil.finish("DaoMaster.createAllTables");
    }
    await StorageUtils.putValue(`${db.name}_upgrade`, false);
    AbstractDao.allTableIsCreateEnd();
  }

  /**
   * drops underlying database table using DAOs.
   * @param db
   * @param ifExists
   * @deprecated since 2.1.1
   * @useinstead DaoMaster#dropAllTablesAsync
   */
  public static dropAllTables(db: Database, ifExists: boolean): void {
    const uniqueKeyForEntities = `${GlobalContext.KEY_CLS_ARRAY}_${db.name}${db.customDir ? '_'+db.customDir : ''}`;
    let entities = GlobalContext.getContext().getValue(uniqueKeyForEntities) as Object[];
    if (entities) {
      for (let entity of entities) {
        BaseDao.dropTable(db, ifExists, entity);
      }
    }
  }

  /**
   * async drops underlying database table using DAOs.
   * @param db
   * @param ifExists
   * @returns Promise<void>
   */
  public static async dropAllTablesAsync(db: Database, ifExists: boolean): Promise<void> {
    const uniqueKeyForEntities = `${GlobalContext.KEY_CLS_ARRAY}_${db.name}${db.customDir ? '_'+db.customDir : ''}`;
    let entities = GlobalContext.getContext().getValue(uniqueKeyForEntities) as Object[];
    if (entities) {
      for (let entity of entities) {
        await BaseDao.dropTableAsync(db, ifExists, entity);
      }
    }
  }

  /**
   * WARNING: Drops all table on Upgrade! Use only during development.
   * Convenience method using a {@link DevOpenHelper}.
   */
  public static async newDevSession(context: any, dbname: string): Promise<DaoSession> {
    let db: Database = await new DevOpenHelper(context, dbname).getWritableDb();
    let daoMaster: DaoMaster = new DaoMaster(db);
    let daoSession: DaoSession = daoMaster.newSession();
    return daoSession;
  }

  constructor(db: any) {
    let cls = db.constructor.name;
    if (cls === 'StandardDatabase') {
      super(db, schema_version);
    } else {
      super(new StandardDatabase(db), schema_version);
    }
    let uniqueKeyForEntities = `${GlobalContext.KEY_CLS_ARRAY}_${db.name}${db.customDir ? '_'+db.customDir : ''}`;
    let entities = GlobalContext.getContext().getValue(uniqueKeyForEntities) as Array<Function>[];
    if (entities) {
      for (let entity of entities) {
        this.registerDaoClass(entity);
      }
    }
  }

  public newSession(type_s = IdentityScopeType.Session): DaoSession {
    return new DaoSession(this.db, type_s, this.daoConfigMap);
  }
}


/**
 * Calls {@link #createAllTables(Database, boolean)} in {@link #onCreate(Database)} -
 */
export abstract class OpenHelper extends DatabaseOpenHelper {
  constructor(context: any, name: string, customDir?: string, factory?: any) {
    super(context, name, schema_version, customDir, factory);
  }

  /**
   * Create Database
   * @param db
   * @useinstead OpenHelper#onCreateDatabase
   */
  public async onCreateDatabase(db: Database): Promise<void> {
    await DaoMaster.createAllTables(db, true);
  }

  /**
   * Upgrade Database
   * @param db
   * @param oldVersion
   * @param newVersion
   * @useinstead DevOpenHelper#onUpgradeDatabase
   */
  public async onUpgradeDatabase(db: Database, oldVersion: number, newVersion: number): Promise<void> {
    let migrations = this.getMigration();
    if (migrations) {
      migrations.forEach(async (migrations_item) => {
        await migrations_item.executeAsync(GlobalContext.getContext().getValue(GlobalContext.KEY_CTX), this.encrypt, this.customDir)
      });
    }
  }

  /**
   * Downgrade Database
   * @param db
   * @param oldVersion
   * @param newVersion
   * @useinstead DevOpenHelper#onDowngradeDatabase
   */
  public async onDowngradeDatabase(db: Database, oldVersion: number, newVersion: number): Promise<void> {
    let migrations = this.getMigration();
    if (migrations) {
      migrations.forEach(async (migrations_item) => {
        await migrations_item.executeAsync(GlobalContext.getContext().getValue(GlobalContext.KEY_CTX), this.encrypt, this.customDir)
      });
    }
  }
}

/** WARNING: Drops all table on Upgrade! Use only during development. */
export class DevOpenHelper extends OpenHelper {
  constructor(context: any, name: string, factory?: any) {
    super(context, name, factory);
  }

  /**
   * Upgrade Database
   * @param db
   * @param oldVersion
   * @param newVersion
   * @useinstead DevOpenHelper#onUpgradeDatabase
   */
  public async onUpgradeDatabase(db: Database, oldVersion: number, newVersion: number): Promise<void> {
    let migrations = this.getMigration();
    if (migrations) {
      migrations.forEach(async (migrations_item) => {
        await migrations_item.execute(GlobalContext.getContext().getValue(GlobalContext.KEY_CTX), this.encrypt)
      });
    }
  }

  /**
   * Downgrade Database
   * @param db
   * @param oldVersion
   * @param newVersion
   * @useinstead DevOpenHelper#onDowngradeDatabase
   */
  public async onDowngradeDatabase(db: Database, oldVersion: number, newVersion: number): Promise<void> {
    let migrations = this.getMigration();
    if (migrations) {
      migrations.forEach(async (migrations_item) => {
        await migrations_item.execute(GlobalContext.getContext().getValue(GlobalContext.KEY_CTX), this.encrypt)
      });
    }
  }
}
